package Algorithm;

import java.util.ArrayList;
import java.util.Collections;

import similarity.PearsonCorrelation;
import type.Movie;
import type.Rating;
import type.User;
import database.MovieLensDatabase;

public class ItemBased
{
	static private class Neighbour implements Comparable<Neighbour>{
		public Double similarity;
		public Double rating;
		public Double ratingMean;
		
		public Neighbour(Double similarity, Double rating, Double ratingMean){
			this.similarity = similarity;
			this.rating = rating;
			this.ratingMean = ratingMean;
		}

		@Override
		public int compareTo(Neighbour o) {
			return this.similarity.compareTo(o.similarity)*-1;
		}
	}
	
	public static double estimateRating(User aUser, Movie aMovie)
	{
		//If the movie is already rated
		if(aMovie.getRatingList().containsKey(aUser))
		{
			return aMovie.getRatingList().get(aUser).getRating();
		}
		
		ArrayList<Neighbour> knn = new ArrayList<Neighbour>();
		
		for (Movie movie : aUser.getRatingList().keySet())
		{
			double similarity = PearsonCorrelation.betweenItem(aMovie, movie);
			
			if(similarity > 0)
			{
				double rating =  movie.getRatingList().get(aUser).getRating();
			
				knn.add(new Neighbour(similarity, rating, movie.getMeanRating()));
			}
		}
		
		Collections.sort(knn);
		double weightedRatingAccumulator = 0.0;
		double absoluteSimilarityAccumulator = 0.0;
		for (int i=0; i<knn.size() && i<30 ; ++i){
			
			weightedRatingAccumulator += (knn.get(i).rating - knn.get(i).ratingMean) * knn.get(i).similarity;
			absoluteSimilarityAccumulator += Math.abs(knn.get(i).similarity);
		}
		
		//Could create a division by 0
		double estimatedRating =
				absoluteSimilarityAccumulator == 0 ? aMovie.getMeanRating() :
					aMovie.getMeanRating() + weightedRatingAccumulator / absoluteSimilarityAccumulator;
				
		estimatedRating = Math.min(estimatedRating, 5);
		estimatedRating = Math.max(estimatedRating, 1);
		
		return estimatedRating;
	}
	
	public static void main(String[] args) {
		MovieLensDatabase db = new MovieLensDatabase("..\\..\\database\\ml-100k");
		
		double errorRateAverage = 0.0;
		for(int i=0; i<5; ++i){
			
			ArrayList<Rating> testRatings = db.selectRatingSet(i);
			double sqError = 0.0;
			for (Rating rating : testRatings) {
				double estimationResult = ItemBased.estimateRating(rating.getUser(), rating.getMovie());
				sqError += Math.pow(estimationResult - rating.getRating(), 2.0);
			}
			double recommendationErrorRate = Math.sqrt(sqError / testRatings.size());
			System.out.println("Error rate for " + i + " is " + recommendationErrorRate);
			errorRateAverage += recommendationErrorRate;
		}
		errorRateAverage /= 5.0;
		System.out.println("Error rate : " + errorRateAverage);
	}
}
